﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using AMM.Model;
using AMM.Model.Store;

namespace AMM.DataAccess.Store {
    public class ProductOutStoreOrderRepository : BaseRepository<ProductOutStoreOrder> {

        private int _pageSize;

        public ProductOutStoreOrderRepository(int pageSize) {
            this._pageSize = pageSize;
        }

        public ProductOutStoreOrder FetchProductOutStoreOrderById(int productOutStoreOrderId) {
            using (var ctx = this.Context) {
                return ctx.ProductOutStoreOrders
                    .Include("ProductOutStoreOrderDetails")
                    .FirstOrDefault(x => x.ProductOutStoreOrderId == productOutStoreOrderId);
            }
        }

        public ProductOutStoreOrder FetchProductOutStoreOrderByIdForView(int productOutStoreOrderId) {
            using (var ctx = this.Context) {
                return ctx.ProductOutStoreOrders
                    .Include("ProductOutStoreOrderDetails")
                    .Include("ProductOutStoreOrderDetails.Product")
                    .FirstOrDefault(x => x.ProductOutStoreOrderId == productOutStoreOrderId);
            }
        }

        public List<ProductOutStoreOrderDetail> FetchProductViewModelListByProductOutStoreOrderId(int productOutStoreOrderId) {
            using (var ctx = this.Context) {
                return ctx.ProductOutStoreOrderDetails
                    .Include("Product")
                    .Include("Product.Basic")
                    .Where(x => x.ProductOutStoreOrderId == productOutStoreOrderId).ToList();
            }
        }

        public void AuditProductOutStoreOrder(ProductOutStoreOrder productOutStoreOrder) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        // Subtract store amount
                        productOutStoreOrder.ProductOutStoreOrderDetails.ToList().ForEach(x => {
                            List<ProductStore> existPSList = ctx.ProductStores
                                .Where(y => y.ProductId == x.ProductId)
                                .OrderBy(y => y.Quantity)
                                .ToList();

                            decimal tempOfQuantity = x.Quantity, restOfQuantity = x.Quantity;
                            foreach (ProductStore ps in existPSList) {
                                tempOfQuantity = tempOfQuantity - ps.Quantity;
                                if (tempOfQuantity > 0) {
                                    ps.OutStoreTotal += ps.Quantity;
                                    ps.Quantity = 0;
                                    ctx.ProductStores.Attach(ps);
                                    ctx.Entry(ps).State = EntityState.Modified;
                                    restOfQuantity = tempOfQuantity;
                                }
                                else {
                                    ps.Quantity -= restOfQuantity;
                                    ps.OutStoreTotal += restOfQuantity;
                                    ctx.ProductStores.Attach(ps);
                                    ctx.Entry(ps).State = EntityState.Modified;
                                    break;
                                }
                            }
                        });

                        ctx.ProductOutStoreOrders.Attach(productOutStoreOrder);
                        ctx.Entry(productOutStoreOrder).State = EntityState.Modified;

                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public void UpdateProductOutStoreOrder(ProductOutStoreOrder productOutStoreOrder) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        productOutStoreOrder.ProductOutStoreOrderDetails.Where(x => x.ClassStatus == ClassStatus.Added).ToList().ForEach(x => {
                            x.ProductOutStoreOrder = productOutStoreOrder;
                            x.ProductOutStoreOrderId = productOutStoreOrder.ProductOutStoreOrderId;
                            x.ProductOutStoreOrderDetailsId = Guid.NewGuid().GetHashCode();
                        });
                        // do not try to merge with above codes, because you can't change the fields which was marked as primary key
                        productOutStoreOrder.ProductOutStoreOrderDetails.ToList().ForEach(x => {
                            ctx.ProductOutStoreOrderDetails.Attach(x);
                            switch (x.ClassStatus) {
                                case ClassStatus.UnChanged:
                                    ctx.ProductOutStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Unchanged;
                                    break;
                                case ClassStatus.Updated:
                                    x.ModifiedBy = productOutStoreOrder.ModifiedBy;
                                    x.ModifiedDatetime = DateTime.Now;
                                    ctx.ProductOutStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Modified;
                                    break;
                                case ClassStatus.Added:
                                    x.CreatedBy = productOutStoreOrder.ModifiedBy;
                                    x.CreatedDatetime = DateTime.Now;
                                    ctx.ProductOutStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Added;
                                    break;
                                case ClassStatus.Deleted:
                                    ctx.ProductOutStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Deleted;
                                    break;
                            }
                        });

                        ctx.ProductOutStoreOrders.Attach(productOutStoreOrder);
                        ctx.Entry(productOutStoreOrder).State = EntityState.Modified;

                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public void DeleteMultipleProductOutStoreOrder(params int[] productOutStoreOrderIds) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        ctx.ProductOutStoreOrderDetails.Where(x => productOutStoreOrderIds.Contains(x.ProductOutStoreOrderId)).ToList().ForEach(x => {
                            ctx.ProductOutStoreOrderDetails.Remove(x);
                        });
                        ctx.ProductOutStoreOrders.Where(x => productOutStoreOrderIds.Contains(x.ProductOutStoreOrderId)).ToList().ForEach(x => {
                            ctx.ProductOutStoreOrders.Remove(x);
                        });
                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public List<ProductOutStoreOrder> FetchProductOutStoreOrderList(ProductOutStoreOrderSearchModel searchModel, int pageIndex) {
            using (var ctx = this.Context) {
                var query = ctx.ProductOutStoreOrders
                    .Include("Basic")
                    .Include("ProductOutStoreOrderDetails")
                    .Include("ProductOutStoreOrderDetails.Product")
                    .Where(x => !string.IsNullOrEmpty(x.Code));

                if (!string.IsNullOrEmpty(searchModel.ProductOutStoreOrderCode)) {
                    query = query.Where(x => x.Code.Contains(searchModel.ProductOutStoreOrderCode));
                }
                if (!string.IsNullOrEmpty(searchModel.ProductCode)) {
                    query = query.Where(x => x.ProductOutStoreOrderDetails.Any(y => y.Product.Code.Contains(searchModel.ProductCode)));
                }
                if (searchModel.ProductOutStoreDateStart.HasValue) {
                    query = query.Where(x => x.ProductOutStoreOrderDetails.Any(y => y.CreatedDatetime >= DbFunctions.TruncateTime(searchModel.ProductOutStoreDateStart.Value)));
                }
                if (searchModel.ProductOutStoreDateEnd.HasValue) {
                    query = query.Where(x => x.ProductOutStoreOrderDetails.Any(y => y.CreatedDatetime <= DbFunctions.TruncateTime(searchModel.ProductOutStoreDateEnd.Value)));
                }
                if (searchModel.ProductOutStoreTypeId.HasValue) {
                    query = query.Where(x => x.OutStoreTypeId == searchModel.ProductOutStoreTypeId.Value);
                }
                if (searchModel.OrderStatus.HasValue) {
                    query = query.Where(x => x.Status == searchModel.OrderStatus);
                }

                searchModel.Records = query.Count();
                pageIndex = (pageIndex - 1) * this._pageSize > searchModel.Records ? 1 : pageIndex;

                return query.OrderByDescending(x => x.CreatedDatetime).Skip((pageIndex - 1) * this._pageSize).Take(this._pageSize).ToList();
            }
        }

        public int GetTheMaxFlowNumber(string startString) {
            using (var ctx = this.Context) {
                ProductOutStoreOrder ProductOutStoreOrder = ctx.ProductOutStoreOrders
                    .Where(x => x.Code.StartsWith(startString))
                    .OrderByDescending(x => x.Code)
                    .FirstOrDefault();
                int maxFlowNumber = ProductOutStoreOrder == null
                    ? 0
                    : Convert.ToInt32(ProductOutStoreOrder.Code.Substring(ProductOutStoreOrder.Code.Length - 2, 2));
                return Math.Max(ctx.ProductOutStoreOrders.Count(x => x.Code.StartsWith(startString)), maxFlowNumber);
            }
        }
    }
}
